#pragma once

#include <unordered_map>

#include <cftc/timemeter/interval.h>

namespace battle::object
{
    class Player;
}

class Map;
// ---------------------------------------------------------
//            Actual Weather in one zone
// ---------------------------------------------------------

namespace game::system
{
    enum WeatherState
    {
        WEATHER_STATE_FINE = 0,
        WEATHER_STATE_LIGHT_RAIN = 3,
        WEATHER_STATE_MEDIUM_RAIN = 4,
        WEATHER_STATE_HEAVY_RAIN = 5,
        WEATHER_STATE_LIGHT_SNOW = 6,
        WEATHER_STATE_MEDIUM_SNOW = 7,
        WEATHER_STATE_HEAVY_SNOW = 8,
        WEATHER_STATE_LIGHT_SANDSTORM = 22,
        WEATHER_STATE_MEDIUM_SANDSTORM = 41,
        WEATHER_STATE_HEAVY_SANDSTORM = 42
    };

    struct WeatherZoneChances;

    /// Weather for one zone
    class Weather
    {
    public:
        Weather(unsigned int zone, const WeatherZoneChances * weatherChances);
        ~Weather() {};

        /// Send Weather to one player
        void SendWeatherUpdateToPlayer(battle::object::Player* player);
        /// Set the weather
        void SetWeather(WeatherType type, float grade, Map const* _map, bool isPermanent);
        /// Update the weather in this zone, when the timer is expired the weather will be rolled again
        bool Update(unsigned int diff, Map const* _map);
        /// Check if a type is valid
        static bool IsValidWeatherType(unsigned int type)
        {
            switch (type)
            {
            case WEATHER_TYPE_FINE:
            case WEATHER_TYPE_RAIN:
            case WEATHER_TYPE_SNOW:
            case WEATHER_TYPE_STORM:
                return true;
            default:
                return false;
            }
        }

    private:
        unsigned int GetSound();
        /// Send SMSG_WEATHER to all players in the zone
        bool SendWeatherForPlayersInZone(Map const* _map);
        /// Calculate new weather
        bool ReGenerate();
        /// Calculate state based on type and grade
        WeatherState GetWeatherState() const;
        // Helper to get the grade between 0..1
        void NormalizeGrade();
        // Helper to log recent state
        void LogWeatherState(WeatherState state) const;

        unsigned int m_zone;
        WeatherType m_type;
        float m_grade;
        cftc::timemeter::Interval m_timer;
        WeatherZoneChances const* m_weatherChances;
        bool m_isPermanentWeather;
    };

    // ---------------------------------------------------------
    //         Weather information hold on one map
    // ---------------------------------------------------------

    /// Weathers for one map
    class WeatherSystem
    {
    public:
        WeatherSystem(Map const* _map);
        ~WeatherSystem();

        Weather* FindOrCreateWeather(unsigned int zoneId);
        void UpdateWeathers(unsigned int diff);

    private:
        Map const* const m_map;

        typedef std::unordered_map<unsigned int /*zoneId*/, Weather*> WeatherMap;
        WeatherMap m_weathers;
    };

    // ---------------------------------------------------------
    //              Global Weather Information
    // ---------------------------------------------------------

#define WEATHER_SEASONS 4
    struct WeatherSeasonChances
    {
        unsigned int rainChance;
        unsigned int snowChance;
        unsigned int stormChance;
    };

    struct WeatherZoneChances
    {
        WeatherSeasonChances data[WEATHER_SEASONS];
    };

    class WeatherMgr
    {
    public:
        WeatherMgr() {};
        ~WeatherMgr() {};

        void LoadWeatherZoneChances();

        WeatherZoneChances const* GetWeatherChances(unsigned int zone_id) const
        {
            WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
            if (itr != mWeatherZoneMap.end())
            {
                return &itr->second;
            }
            else
            {
                return NULL;
            }
        }

    private:
        typedef std::unordered_map<unsigned int /*zoneId*/, WeatherZoneChances> WeatherZoneMap;
        WeatherZoneMap      mWeatherZoneMap;
    };
}